package com.punch.course.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.punch.course.asyn.CourseAsync;
import com.punch.course.mapper.TimeTaskMapper;
import com.punch.course.model.TimeTask;
import com.punch.course.service.TimeTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;

@Service
@Slf4j
public class TimeTaskServiceImpl implements TimeTaskService {

    @Resource
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Resource
    private TimeTaskMapper timeTaskMapper;

    private Map<Long, ScheduledFuture<?>> futureMap = new ConcurrentHashMap<>();

    @Autowired
    @Lazy
    private CourseAsync courseAsync;


    /**
     * 启动时候初始化任务
     */
    @Override
    public void initCron() {
        List<TimeTask> timeTasks = timeTaskMapper.selectList(null);
        log.info("程序启动 ==> 初始化所有任务开始 ！size={}", timeTasks.size());
        if (CollUtil.isEmpty(timeTasks)) {
            return;
        }
        for (TimeTask timeTask : timeTasks) {
            //任务 key
            Long taskKey = timeTask.getId();
            //校验是否已经启动
            if (this.isStart(taskKey)) {
                continue;
            }
            //启动任务
            this.doStartTask(taskKey, timeTask.getType(), timeTask.getAssociateId(), timeTask.getAssociateType(), timeTask.getTimeCron());
        }
        log.info("程序启动 ==> 初始化所有任务结束 ！size={}", timeTasks.size());
    }

    /**
     * 新增定时任务
     *
     * @param timeTaskId    任务id
     * @param associateType 类型
     * @param cron          表达式
     */
    @Override
    public void addCron(Long timeTaskId, Integer type, Long associateId, Integer associateType, String cron) {
        log.info("添加定时任务:[{}],类型:[{}]，表达式:[{}]", timeTaskId, associateType, cron);
        ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(new TimeTaskRunnable(timeTaskId, type, associateId, associateType), new CronTrigger(cron));
        futureMap.put(timeTaskId, future);
    }

    /**
     * 修改定时任务
     *
     * @param timeTaskId 任务id
     * @param cron       表达式
     */
    @Override
    public void updateCron(Long timeTaskId, Integer type, Long associateId, Integer associateType, String cron) {
        log.info("修改定时任务:[{}],类型:[{}]，表达式:[{}]", timeTaskId, associateType, cron);
        //先暂停
        stopCron(timeTaskId);
        if (futureMap.get(timeTaskId) != null) {
            //重新开启
            addCron(timeTaskId, type, associateId, associateType, cron);
        }
    }

    /**
     * 结束定时任务
     *
     * @param timeTaskId 任务id
     */
    @Override
    public void stopCron(Long timeTaskId) {
        if (futureMap.get(timeTaskId) != null) {
            futureMap.get(timeTaskId).cancel(true);
        }
    }

    /**
     * 删除定时任务
     *
     * @param timeTaskId 任务id
     */
    @Override
    public void removeCron(Long timeTaskId) {
        stopCron(timeTaskId);
        if (futureMap.get(timeTaskId) != null) {
            //重新开启
            futureMap.remove(timeTaskId);
        }
    }

    /**
     * 任务是否已经启动
     *
     * @param timeTaskId 任务id
     * @return boolean
     */
    private Boolean isStart(Long timeTaskId) {
        //校验是否已经启动
        if (futureMap.containsKey(timeTaskId)) {
            return !futureMap.get(timeTaskId).isCancelled();
        }
        return false;
    }

    /**
     * 执行启动任务
     *
     * @param timeTaskId 任务id
     * @param type       类型
     * @param taskCron   表达式
     */
    private void doStartTask(Long timeTaskId, Integer type, Long associateId, Integer associateType, String taskCron) {
        log.info("======任务id [ {} ] 类型:[{}],cron=[{}]", timeTaskId, associateType, taskCron);
        ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(new TimeTaskRunnable(timeTaskId, type, associateId, associateType), new CronTrigger(taskCron));
        //将启动的任务放入 map
        futureMap.put(timeTaskId, future);
    }

    /**
     * 具体的任务
     */
    private class TimeTaskRunnable implements Runnable {
        private Long timeTaskId;
        private Integer type;
        private Long associateId;
        private Integer associateType;

        TimeTaskRunnable(Long timeTaskId, Integer type, Long associateId, Integer associateType) {
            this.timeTaskId = timeTaskId;
            this.type = type;
            this.associateId = associateId;
            this.associateType = associateType;
        }

        @Override
        public void run() {
            log.info("班级定时任务,任务id:{}", timeTaskId);
            if (type == 0) {
                //删除该任务
                removeCron(timeTaskId);
                if (associateType == 0) {
                    courseAsync.toSignUp(timeTaskId, associateId);//开始报名
                }
                if (associateType == 1) {
                    courseAsync.toOpenClass(timeTaskId, associateId);//报名结束,待开班
                }
                if (associateType == 2) {
                    courseAsync.openClass(timeTaskId, associateId);//开班
                }
            }
        }
    }

}
